#[cfg(test)]
mod tests {
    // 关于代换元变量 (substitution，这里指把已经进行宏解析的 token 再次传给宏) ，
    // 常常让人惊讶的一面是，尽管 很像 是根据标记 (token) 进行代换的，但事实并非如此 ——代换基于已经解析的 AST 节点。
    #[test]
    fn test_substitution() {
        macro_rules! capture_then_match_tokens {
            ($e:expr) => {
                match_tokens!($e)
            };
        }

        macro_rules! match_tokens {
            ($a:tt + $b:tt) => {
                "got an addition"
            };
            (($i:ident)) => {
                "got an identifier"
            };
            ($($other:tt)*) => {
                "got something else"
            };
        }

        // 通过解析已经传入 AST 节点的输入，代换的结果变得 很稳定：你再也无法检查其内容了， 也不再匹配内容。
        println!(
            "{}\n{}\n{}\n",
            match_tokens!((caravan)), // got an identifier
            match_tokens!(3 + 6),     // got an addition
            match_tokens!(5)
        ); // got something else
        println!(
            "{}\n{}\n{}",
            capture_then_match_tokens!((caravan)), // got something else
            capture_then_match_tokens!(3 + 6),     // got something else
            capture_then_match_tokens!(5)
        ); // got something else

        // 另一个例子可能也会很令人困惑：
        // 避免这个意外情况的唯一方式就是使用 tt、ident 或者 lifetime 分类符。 每当你捕获到除此之外的分类符，结果将只能被用于直接输出。
        // 比如这里使用的 stringify!2，它是一条内置于编译器的语法拓展 （查看源码可知）， 将所有输入标记结合在一起，作为单个字符串输出。
        macro_rules! capture_then_what_is {
            (#[$m:meta]) => {what_is!(#[$m])};
        }

        macro_rules! what_is {
            (#[no_mangle]) => {"no_mangle attribute"};
            (#[inline]) => {"inline attribute"};
            ($($tts:tt)*) => {concat!("something else (", stringify!($($tts)*), ")")};
        }

        println!(
            "{}\n{}\n{}\n{}",
            what_is!(#[no_mangle]), // no_mangle attribute
            what_is!(#[inline]),    // inline attribute
            capture_then_what_is!(#[no_mangle]),    // something else (#[no_mangle])
            capture_then_what_is!(#[inline]),       // something else (#[inline])
        );
    }
}
